package com.ape.mapsa.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.MenuModel;

import com.ape.mapsa.produccion.model.Formula;
import com.ape.mapsa.produccion.model.FormulaMp;
import com.ape.mapsa.produccion.model.Item;
import com.ape.mapsa.produccion.service.IProduccionService;

@ManagedBean(name = "formulasMB")
@ViewScoped
public class FormulasBean extends BeanCRUD implements Serializable {

	private static final long serialVersionUID = -1865358477053230245L;

	private static final Logger log = Logger.getLogger(FormulasBean.class);

	@ManagedProperty(value = "#{produccionService}")
	private IProduccionService prodService;

	private List<Formula> formulasAutocomp;
	private Formula selectedFormula = new Formula();
	private String selectedFormulaStr;

	private List<Formula> formulas;
	private List<Formula> formulasFiltered;

	private Long rawMaterialTempId = 0L;
	private Formula newFormula;

	private List<Item> invRawMaterials;
	private Item invRawMaterialSelected;
	private String invRawMaterialSelectedStr;

	private boolean editing;

	private boolean showAbm;

	private Long selectedId;

	@PostConstruct
	public void initialize() {
		formulas = prodService.getAllFormulas();
	}

	public void rowSelected(SelectEvent event) {
		log.info("formula seleccionada " + selectedFormula.getId());
	}

	public List<String> completeMp(String query) {
		List<String> result = new ArrayList<String>();
		invRawMaterials = prodService.getMpByQuery(query);
		for (Item rowMat : invRawMaterials) {
			result.add(rowMat.getDescripcion());
		}
		return result;
	}

	public void editarItem(ActionEvent actionEvent) {
		log.info("Editando Formula " + selectedFormula);
		newFormula = selectedFormula;
		if (newFormula != null) {
			editing = true;
		}
	}

	public void resetItem() {
		log.info("Reseting item data");
		newFormula = new Formula();
		editing = false;
		showAbm = false;
	}

	/**
	 * Esta función guarda las modificaciones de la formula seleccionada<br>
	 * O si la formula seleccionada es nueva crea la formula
	 */
	public void saveFormulaMps() {
		log.debug("Saving Formula");

		boolean validationFaild = validateFormula();

		if (validationFaild) {
			log.debug("Fallo la validacion");
			RequestContext.getCurrentInstance().addCallbackParam("isValid", false);
			return;
		} else {
			log.debug("Validation pass");
			RequestContext.getCurrentInstance().addCallbackParam("isValid", true);
		}

		if (selectedFormula.getId() == null) {
			log.debug("Formula Nueva");
			// Save formula
			prodService.saveFormula(selectedFormula);
			// Save all materials of the formula
			for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
				prodService.saveFormulaMp(formulaMp);
			}

		} else {
			log.debug("Formula Existente");
			// Update formula
			prodService.updateFormula(selectedFormula);
			Formula originalFormula = prodService.getFormulaById(selectedFormula.getId());
			originalFormula.setFormulaMps(prodService.getMpForFormula(originalFormula));
			for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
				// Svae all new added formulas
				log.debug("Svae all new added formulas");
				if (formulaMp.getId() < 0) {
					log.debug("Svaeing " + formulaMp.getId());
					prodService.saveFormulaMp(formulaMp);
				}
			}

			for (FormulaMp originalFmp : originalFormula.getFormulaMps()) {
				boolean found = false;
				for (FormulaMp selectedFmp : selectedFormula.getFormulaMps()) {
					if (selectedFmp.getId() > 0 && selectedFmp.getId().equals(originalFmp.getId()) && !originalFmp.equals(selectedFmp)) {
						log.debug("Changed formula material " + selectedFmp.getId());
						// Changed formula material
						found = true;
						prodService.updateFormulaMp(selectedFmp);
					} else if (selectedFmp.getId() > 0 && selectedFmp.getId().equals(selectedFmp.getId()) && selectedFmp.equals(originalFmp)) {
						log.debug(" Mp still there and has not changed " + selectedFmp.getId());
						// Mp still there and has not changed
						found = true;
					} else if (selectedFmp.getId() < 0) {
						log.debug("This is new one.. no need to update " + selectedFmp.getId());
						found = true;
					}
				}
				if (!found) {
					log.debug("formula not found deleting " + originalFmp.getId());
					prodService.deleteFormulaMp(originalFmp);
				}
			}
		}
	}

	private boolean validateFormula() {
		boolean validationFaild = false;
		// Product Order has not changed in the prod table.
		// CHeck if there is repeted raw materials
		Set<String> repetedMps = new HashSet<String>();
		for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
			repetedMps.add(formulaMp.getItem().getDescripcion());
		}
		if (repetedMps.size() != selectedFormula.getFormulaMps().size()) {
			validationFaild = true;
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Materia Prima", "duplicada en la formula");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
		// End Cheking dupliacate materials

		// Check for 100%
		int percent = 0;
		for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
			percent = formulaMp.getPercent() + percent;
		}
		if (percent != 100) {
			validationFaild = true;
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Porcentaje incorrecto", "la sumatoria de porcentajes difiere de 100");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
		// Done checking 100%

		// CHeck formula name uniqueness
		Formula byDesc = prodService.getFormulaByDescription(selectedFormula.getDescription());
		if (selectedFormula.getId() != null && byDesc != null && !byDesc.getId().equals(selectedFormula.getId())) {
			validationFaild = true;
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Nombre de formula", "Duplicado");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		} else if (selectedFormula.getId() == null && byDesc != null) {
			validationFaild = true;
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Nombre de formula", "Duplicado");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
		// END CHECK FORMULA UNIQUENESS
		return validationFaild;
	}

	public void addRawMaterial(ActionEvent event) {
		FormulaMp e = new FormulaMp();
		e.setItem(new Item());
		rawMaterialTempId = rawMaterialTempId - 1;
		e.setId(rawMaterialTempId);
		if (selectedFormula == null) {
			log.debug("No selected formula creating new from scratch");
			selectedFormula = new Formula();
		}
		selectedFormula.addFormulaMp(e);
		e.setFormula(selectedFormula);
	}

	public void handleFormulaSelection(SelectEvent event) {
		log.debug("formula selected " + selectedFormulaStr);
		if (selectedFormulaStr != null) {
			for (Formula frmula : formulasAutocomp) {
				if (frmula.getDescription().equals(selectedFormulaStr)) {
					selectedFormula = frmula;
					selectedFormula.setFormulaMps(prodService.getMpForFormula(selectedFormula));
				}
			}
		}
	}

	public List<String> completeFormulas(String query) {
		List<String> result = new ArrayList<String>();
		formulasAutocomp = prodService.getFormulas(query);
		for (Formula formula : formulasAutocomp) {
			result.add(formula.getDescription());
		}
		return result;
	}

	public void handleMpSelection(SelectEvent event) {
		log.debug("mp selected selected " + event.getObject());

		for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
			log.debug("processing formula Mp: " + formulaMp.getId());
			if (event.getObject().toString().startsWith(formulaMp.getId().toString())) {
				log.debug("formula mp id equals selected row id");
				for (Item mp : invRawMaterials) {
					log.debug("processing row material: " + mp);
					if (event.getObject().toString().endsWith(mp.getDescripcion())) {
						log.debug("form all autocmplete list this is the one user selected " + mp);
						formulaMp.setItem(mp);
						break;
					}
				}
			}
		}
		log.debug("finished");
	}

	public void deleteForumlaItem(Long id) {
		log.debug("Deleting FormulaMp ID: " + id);
		List<FormulaMp> mps = selectedFormula.getFormulaMps();
		log.debug("Formula mps size: " + mps.size());
		for (FormulaMp formulaMp : mps) {
			if (formulaMp.getId().equals(id)) {
				log.debug("id found deleting... ");
				mps.remove(formulaMp);
				break;
			}
		}
		log.debug("Formula mps size after: " + mps.size());
	}

	public void saveAsNewFormula(ActionEvent event) {
		log.debug("Save as New Formula");
		selectedFormula.setId(null);
		boolean validationFaild = validateFormula();

		if (validationFaild) {
			log.debug("Fallo la validacion");
			RequestContext.getCurrentInstance().addCallbackParam("isValid", false);
			return;
		} else {
			log.debug("Validation pass");
			RequestContext.getCurrentInstance().addCallbackParam("isValid", true);
		}

		log.debug("Formula Nueva");
		// Save formula
		prodService.saveFormula(selectedFormula);
		// Save all materials of the formula
		for (FormulaMp formulaMp : selectedFormula.getFormulaMps()) {
			formulaMp.setId(null);
			formulaMp.setFormula(selectedFormula);
			prodService.saveFormulaMp(formulaMp);
		}
	}

	@Override
	public void save(ActionEvent event) {
		if (selectedFormula != null) {
			if (!selectedId.equals(selectedFormula.getId())) {
				for (Formula formula : formulas) {
					if (formula.getId().equals(selectedId)) {
						selectedFormula = formula;
					}
				}
			}

			saveFormulaMps();
			if (RequestContext.getCurrentInstance().getCallbackParams().get("isValid") != null
					&& (boolean) RequestContext.getCurrentInstance().getCallbackParams().get("isValid")) {
				FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Formula", "Guardad satisfactoriamente");
				FacesContext.getCurrentInstance().addMessage(null, msg);
			}

			RequestContext.getCurrentInstance().update("addFormulaForm:abmPanel");
			RequestContext.getCurrentInstance().update("addFormulaForm:items:items");
		}
	}

	@Override
	public void update(ActionEvent event) {
		FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Implementación", "Pendiente");
		FacesContext.getCurrentInstance().addMessage(null, msg);
	}

	@Override
	public void create(ActionEvent event) {
		log.info("Nueva Formula");
		newFormula = new Formula();
		selectedFormula = new Formula();
		log.debug("item reseted: " + newFormula);
		showAbm = true;
		editing = false;
		RequestContext.getCurrentInstance().update("addFormulaForm:abmPanel");
		RequestContext.getCurrentInstance().scrollTo("addFormulaForm:abmPanel");
	}

	@Override
	public void delete(ActionEvent event) {
		if (selectedFormula.getId() != null) {
			log.debug("Deleting formula");
			selectedFormula.setState(0);
			prodService.updateFormula(selectedFormula);
			RequestContext.getCurrentInstance().update("addFormulaForm:abmPanel");
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Formula", "Desactivada");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		} else {
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Desactivación",
					"Debe seleccionar una formula para realizar esta operación");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
	}

	public void activateFormula(ActionEvent event) {
		if (selectedFormula.getId() != null) {
			log.debug("Deleting formula");
			selectedFormula.setState(1);
			prodService.updateFormula(selectedFormula);
			RequestContext.getCurrentInstance().update("addFormulaForm:abmPanel");
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Formula", "Activada");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
	}

	@Override
	public void edit(ActionEvent event) {
		if (selectedFormula.getId() != null) {
			log.info("Editando Formula " + selectedFormula);
			newFormula = selectedFormula;
			showAbm = true;
			selectedId = selectedFormula.getId();
			if (newFormula != null) {
				editing = true;
			}
			RequestContext.getCurrentInstance().update("addFormulaForm:abmPanel");
			RequestContext.getCurrentInstance().scrollTo("addFormulaForm:abmPanel");
		} else {
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Desactivación",
					"Debe seleccionar una formula para realizar esta operación");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
	}

	@Override
	public MenuModel getMenuModel() {
		return menuModel;
	}

	public List<Formula> getFormulas() {
		return formulas;
	}

	public void setFormulas(List<Formula> formulas) {
		this.formulas = formulas;
	}

	public List<Formula> getFormulasFiltered() {
		return formulasFiltered;
	}

	public void setFormulasFiltered(List<Formula> formulasFiltered) {
		this.formulasFiltered = formulasFiltered;
	}

	public Formula getSelectedFormula() {
		return selectedFormula;
	}

	public void setSelectedFormula(Formula selectedFormula) {
		this.selectedFormula = selectedFormula;
	}

	public Formula getNewFormula() {
		return newFormula;
	}

	public void setNewFormula(Formula newFormula) {
		this.newFormula = newFormula;
	}

	public boolean isEditing() {
		return editing;
	}

	public void setEditing(boolean editing) {
		this.editing = editing;
	}

	public boolean isShowAbm() {
		return showAbm;
	}

	public void setShowAbm(boolean showAbm) {
		this.showAbm = showAbm;
	}

	public IProduccionService getProdService() {
		return prodService;
	}

	public void setProdService(IProduccionService prodService) {
		this.prodService = prodService;
	}

	public Item getInvRawMaterialSelected() {
		return invRawMaterialSelected;
	}

	public void setInvRawMaterialSelected(Item invRawMaterialSelected) {
		this.invRawMaterialSelected = invRawMaterialSelected;
	}

	public String getInvRawMaterialSelectedStr() {
		return invRawMaterialSelectedStr;
	}

	public void setInvRawMaterialSelectedStr(String invRawMaterialSelectedStr) {
		this.invRawMaterialSelectedStr = invRawMaterialSelectedStr;
	}

	public List<Formula> getFormulasAutocomp() {
		return formulasAutocomp;
	}

	public void setFormulasAutocomp(List<Formula> formulasAutocomp) {
		this.formulasAutocomp = formulasAutocomp;
	}

	public String getSelectedFormulaStr() {
		return selectedFormulaStr;
	}

	public void setSelectedFormulaStr(String selectedFormulaStr) {
		this.selectedFormulaStr = selectedFormulaStr;
	}

	public Long getRawMaterialTempId() {
		return rawMaterialTempId;
	}

	public void setRawMaterialTempId(Long rawMaterialTempId) {
		this.rawMaterialTempId = rawMaterialTempId;
	}

	public List<Item> getInvRawMaterials() {
		return invRawMaterials;
	}

	public void setInvRawMaterials(List<Item> invRawMaterials) {
		this.invRawMaterials = invRawMaterials;
	}

}
